/*
 * Created on 2008-06-06
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
package elka.isi.operator;

import elka.isi.gene.AnimalGenotypeBuilder;
import elka.isi.individual.AnimalIndividual;
import elka.isi.util.RWUtils;
import org.evolvica.core.*;
import org.evolvica.core.recombine.iarray.MultipointCrossover;
import org.evolvica.util.RandomUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * @author Zibi
 *
 * To change the template for this generated type comment go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
public class AnimalMultipointCrossover extends MultipointCrossover {

	//private static int DEFAULT_CHILDREN_COUNT = 3;
	private Map individualBuilderMap = new HashMap();
	private Map geneBuilderMap = new HashMap();
	

	//private int childrenCount = DEFAULT_CHILDREN_COUNT;

	public IIndividual[] recombine(IIndividual[] parents) {
		Class species = ((AnimalIndividual) parents[0]).getSpecies_OLD();
		IIndividualBuilder builder =
			(IIndividualBuilder) individualBuilderMap.get(species);
		AnimalGenotypeBuilder genotypeBuilder =
			(AnimalGenotypeBuilder) geneBuilderMap.get(species);
		if (builder == null)
			throw new IllegalStateException(
				"Builder for species: " + species + " not set!");
		if (genotypeBuilder == null)
			throw new IllegalStateException(
				"GeneBuilder for species: " + species + " not set!");
//		TODO: gdzies tutaj trzeba by ustawiac liczbe dzieci, jesli
//		mialaby byc zalezna od gatunku.
		int[][] parentGenes = new int[parents.length][];
		for (int i = 0; i < parents.length; i++) {
			parentGenes[i] =
				((IntegerArrayGene) parents[i].getGenotype()).getValue();
		}
		int[][] childGenes = recombine(species, parentGenes, (AnimalIndividual) parents[0], (AnimalIndividual) parents[1]);
		IIndividual[] children = new IIndividual[childGenes.length];
		for (int i = 0; i < children.length; i++) {
			children[i] = builder.newIndividual();
			children[i].setGenotype(genotypeBuilder.newGenotype(childGenes[i]));
		}
		return children;
	}

	public void setIndividualBuilder(
		Class individualClass,
		IIndividualBuilder builder) {
		individualBuilderMap.put(individualClass, builder);
	}

	public void setGeneBuilder(
		Class individualClass,
		AnimalGenotypeBuilder builder) {
		geneBuilderMap.put(individualClass, builder);
	}


	/**
	 * Wybieranie rodzicow do rozmnazania.
	 * Nalezy uwazac, aby rodzice byli tego samego gatunku :)
	 * W tej chwili wszystkie osobniki sie rozmnazaja (chyba ze 
	 * jakis nie ma pary)
	 */
	public Object process( Object input ) {
		int required = getRequiredParents(); // ilu rodzicow bedzie
		IIndividual[] parents = new IIndividual[required];
		IIndividualSet childrenSet = new IndividualArray();
		
		
		IIndividualSet rabbits = RWUtils.getRabbits((IIndividualSet) input);
		IIndividualSet wolves = RWUtils.getWolves((IIndividualSet) input);
		IIndividualSet roes = RWUtils.getRoes((IIndividualSet) input);

        ISetIterator itw = wolves.iterator();
		ISetIterator itr = rabbits.iterator();
        ISetIterator its = roes.iterator();

        boolean breakCondition = false; // stopuje gdy koniec rodzicow zparowanych
		while( itw.hasNext() ) {
			for ( int i = 0; i < required; i++ ) {
				if ( itw.hasNext() ) {
					parents[i] = itw.next();
				} else {
					breakCondition = true;
					break;
				}
			}
			if ( breakCondition == false ) {
				childrenSet.add( recombine( parents ) );
			} else {
				break;
			}
		}

        breakCondition = false;
        while (its.hasNext()) {
            for (int i = 0; i < required; i++) {
                if (its.hasNext()) {
                    parents[i] = its.next();
                } else {
                    breakCondition = true;
                    break;
                }
            }
            if (breakCondition == false) {
                IIndividual[] roesArray = recombine(parents);
                childrenSet.add(roesArray);
            } else {
                break;
            }
        }

        breakCondition = false;
		while( itr.hasNext() ) {
			for ( int i = 0; i < required; i++ ) {
				if ( itr.hasNext() ) {
					parents[i] = itr.next();
				} else {
					breakCondition = true;
					break;
				}
			}
			if ( breakCondition == false ) {
				childrenSet.add( recombine( parents ) );
			} else {
				break;
			}
		}
		
		

        //RWUtils.getActualStats(childrenSet);
		return childrenSet;
	}


	/**
	 * Metoda dostaje 2 rodzicow i zwraca liczbe dzieci zalezna od childrenCount.
	 * Rekombinacja polega na losowym wyborze rodzica, od ktorego dany gen 
	 * zostanie pobrany.
	 */
	protected int[][] recombine(Class species, int[][] parents, AnimalIndividual mama, AnimalIndividual tata) {

//        int childrenCount;
//        if(species.equals(RabbitIndividual.class))
//            childrenCount = 5;
//        else if(species.equals(RoeIndividual.class))
//            childrenCount = 4;
//        else if(species.equals(WolfIndividual.class))
//            childrenCount = 3;
//        else
//            throw new IllegalArgumentException();

        double childrenEstimation = (mama.getReproductionScore() + tata.getReproductionScore()) / 2.0;
        if(childrenEstimation < 0) childrenEstimation = 0;
        int childrenCount = (int) childrenEstimation + 1;

        if (childrenCount > 10 || childrenCount < 1)
            System.out.println("Warning! Children numeber: " + childrenCount);
        
        int size;
		if (parents[0].length > parents[1].length) {
			size = parents[0].length;
		} else {
			size = parents[1].length;
		}
//		tablica losowych 0 i 1, okreslajacych po kim maly ma dany gen
		int[][] geneCrossingTable = new int[childrenCount][size];
		for (int i = 0; i < childrenCount; i++) {
			for (int j = 0; j < size; j++) {
				geneCrossingTable[i][j] = RandomUtils.randomInt(2);
			}
			 
		}
//		kopiowanie genow rodzicow, wg tablicy geneCrossingTable
		int[][] children = new int[childrenCount][size];
		for (int i = 0; i < childrenCount; i++) {
			for (int j = 0; j < size; j++) {
				if(geneCrossingTable[i][j] == 0)
					children[i][j] = parents[0][j];
				else
				children[i][j] = parents[1][j];
			}
		}
		return children;
	}
}
